package org.zeapp.opencloud.framework.chunking;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

import org.zeapp.opencloud.framework.preference.Preference;

/**
 * The basic service for managing a set of Chunking Drivers. Applications can
 * declare their driver using ServiceLoader META-INF file or call explicitly
 * Class.forName() on their Chunking Drivers to allows them to register to this
 * manager.
 * 
 * @author Amphiprion
 * 
 */
public class ChunkingDriverManager {
	private static boolean initialized = false;

	/** The registered drivers **/
	private static List<ChunkingDriver> chunkerDrivers = new ArrayList<ChunkingDriver>();

	/**
	 * Registers the given driver with the DriverManager. A newly-loaded driver
	 * class should call the method registerDriver to make itself known to the
	 * DriverManager.
	 * 
	 * @param driver
	 *            the driver to register
	 */
	public static synchronized void registerDriver(ChunkingDriver driver) {
		if (!initialized) {
			initialize();
		}
		chunkerDrivers.add(driver);
	}

	/**
	 * Drops a driver from the DriverManager's list.
	 * 
	 * @param driver
	 *            the driver to drop
	 */
	public static synchronized void deregisterDriver(ChunkingDriver driver) {
		if (!initialized) {
			initialize();
		}
		chunkerDrivers.remove(driver);
	}

	/**
	 * Return all registered drivers.
	 * 
	 * @return all registered drivers
	 */
	public static List<ChunkingDriver> getDrivers() {
		if (!initialized) {
			initialize();
		}
		return new ArrayList<ChunkingDriver>(chunkerDrivers);
	}

	/**
	 * Attempts to locate a driver that understands the given identifier. The
	 * DriverManager attempts to select an appropriate driver from the set of
	 * registered chunking drivers.
	 * 
	 * @param identifier
	 *            the identifier of the needed driver
	 * @return the chunking driver managing the given identifier
	 * @throws ChunkingException
	 *             if a chunking exception occurs
	 */
	public static synchronized ChunkingDriver getDriver(String identifier)
			throws ChunkingException {
		if (!initialized) {
			initialize();
		}
		for (ChunkingDriver driver : chunkerDrivers) {
			if (driver.accepts(identifier)) {
				return driver;
			}
		}

		throw new ChunkingException("No suitable driver for identifier ["
				+ identifier + "]", "ZECHK00001");

	}

	/**
	 * Attempts to retrieve the chunking service for the given identifier.
	 * 
	 * @param identifier
	 *            Attempts to retrieve the chunking service for the given
	 *            identifier. It is equivalent to
	 *            "getDriver(identifier).getChunker(identifier, preference)".
	 * @param preference
	 *            the preference to use.
	 * @return the chunking service managing the given identifier
	 * @throws ChunkingException
	 *             if a chunking exception occurs
	 */
	public static synchronized Chunking getChunking(String identifier,
			Preference preference) throws ChunkingException {
		if (!initialized) {
			initialize();
		}
		return getDriver(identifier).getChunking(preference);
	}

	/**
	 * Use the ServiceLoader to register declared service.
	 */
	private static void initialize() {
		if (initialized) {
			return;
		}
		System.out.println("Load ChunkDriver implementation via ServiceLoader");
		initialized = true;
		ServiceLoader<ChunkingDriver> ldr = ServiceLoader
				.load(ChunkingDriver.class);
		for (ChunkingDriver provider : ldr) {
			System.out.println(" found: " + provider.getClass());
		}
	}
}
